#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h> // file option
#include <dirent.h>
// #include <iostream>
// #include <sstream>
// #include <vector>
// #include <list>
// #include <map>
// #include <thread>
// #include <string>
// #include <iomanip>
// #include <fstream>
// #include <vector>
// #include <set>
// #include <algorithm>
// #include <unordered_map>
// #include <mutex>
// #include <atomic>
// #include <bits/wordsize.h>
// #include <bits/timesize.h>
//需要添加串口头文件。
#include <termios.h> /* POSIX terminal control definitions */

//备注这里的flow_ctrl=0是cxx规则。如果不填，使用默认值0
int openSerialPort(const char *filename,int speed,int databits,int stopbits,int parity, int flow_ctrl)    
{         
    int i;  
    int fd = -1;  
    int statusflag = -1;    
    int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300};    
    int name_arr[] = {115200,  57600, 38400, 19200,  9600,  4800,  2400,  1200,  300};    

    struct termios options;    
    fd = open(filename, O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd < 0)
    {
        return -1;
    }
    /*  tcgetattr(fd,&options)得到与fd指向对象的相关参数，并将它们保存于options,该函数还可以测试配置是否正确，
    该串口是否可用等。若调用成功，函数返回值为0，若调用失败，函数返回值为1.  */    
    if( tcgetattr( fd,&options)  !=  0)    
    {    
        perror("SetupSerial 1");          
        goto closeFilePoint;//return(FALSE);     
    }    

    //设置串口输入波特率和输出波特率    
    for (i= 0;  i < (int)(sizeof(speed_arr) / sizeof(int));  i++)    
    {    
        if  (speed == name_arr[i])    
        {
            cfsetispeed(&options, speed_arr[i]);     
            cfsetospeed(&options, speed_arr[i]);      
        }    
    }         
    //修改控制模式，保证程序不会占用串口    
    options.c_cflag |= CLOCAL;    
    //修改控制模式，使得能够从串口中读取输入数据    
    options.c_cflag |= CREAD;    

    //设置数据流控制    
    switch(flow_ctrl)    
    {    
        case 0://不使用流控制    
            options.c_cflag &= ~CRTSCTS;    
            break;       

        case 1://使用硬件流控制    
            options.c_cflag |= CRTSCTS;    
            break;    
        case 2://使用软件流控制    
            options.c_cflag |= IXON | IXOFF | IXANY;    
            break;    
    }    
    //设置数据位    
    //屏蔽其他标志位    
    options.c_cflag &= ~CSIZE;    
    switch (databits)    
    {      
        case 5:    
            options.c_cflag |= CS5;    
            break;    
        case 6:    
            options.c_cflag |= CS6;    
            break;    
        case 7:        
            options.c_cflag |= CS7;    
            break;    
        case 8:        
            options.c_cflag |= CS8;    
            break;      
        default:       
            fprintf(stderr,"Unsupported data size\n");    
            goto closeFilePoint;//return(FALSE);     
    }    
    //设置校验位    
    switch (parity)    
    {      
        case 'n':    
        case 'N': //无奇偶校验位。    
            options.c_cflag &= ~PARENB;     
            options.c_iflag &= ~INPCK;        
            break;     
        case 'o':      
        case 'O'://设置为奇校验        
            options.c_cflag |= (PARODD | PARENB);     
            options.c_iflag |= INPCK;                 
            break;     
        case 'e':     
        case 'E'://设置为偶校验      
            options.c_cflag |= PARENB;           
            options.c_cflag &= ~PARODD;           
            options.c_iflag |= INPCK;
            options.c_iflag |= IGNPAR;
            // options.c_iflag &= ~IGNPAR;
            options.c_iflag |= PARMRK;
            //options.c_iflag &= ~PARMRK;
            break;    
        case 's':    
        case 'S': //设置为空格     
            options.c_cflag &= ~PARENB;    
            options.c_cflag &= ~CSTOPB;    
            break;     
        default:      
            fprintf(stderr,"Unsupported parity\n");        
            goto closeFilePoint;//return(FALSE);     
    }     
    // 设置停止位     
    switch (stopbits)    
    {      
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;     
        case 2:
            options.c_cflag |= CSTOPB;
            break;    
        default:
            fprintf(stderr,"Unsupported stop bits\n");     
            goto closeFilePoint;//return(FALSE);     
    }    

    //修改输出模式，原始数据输出    
    options.c_oflag &= ~(OPOST | ONLCR);    
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    
    options.c_lflag &= ~(ISIG | ICANON);    
    options.c_iflag &= ~(BRKINT | ICRNL | ISTRIP | IXON);
    //设置等待时间和最小接收字符    
    // options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */      
    // options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */    

    printf("%x,%x,%x\n", options.c_cflag, options.c_iflag, options.c_oflag);
    //如果发生数据溢出，接收数据，但是不再读取 刷新收到的数据但是不读    
    tcflush(fd,TCIFLUSH);

    //激活配置 (将修改后的termios数据设置到串口中）    
    if (tcsetattr(fd,TCSANOW,&options) != 0)      
    {    
        perror("com set error!\n");      
        goto closeFilePoint;//return(FALSE);     
    }
    statusflag = 0;
closeFilePoint:
    if(statusflag)
    {
        if(fd >= 0)
        {
            close(fd);
        }
        fd = -1;
    }
    return fd;     
}

int main()
{
    //linux是通过文件描述符来操作文件，sock等。打开：open，读:read，写：write，记得不用了close。
    //我这里串口名是"/dev/ttyUSB0"，需要以实际情况输入串口名。
    //我做测试是把串口的RX和TX短接，所以先发送，然后读取看是不是一致。所以usleep(100 * 1000);延时了100ms，以便发送成功。
    int sfd = openSerialPort("/dev/ttyUSB0", 115200, 8, 1, 'n', 0);
    printf("open id = %d\n", sfd);
    if(sfd < 0)
    {
        printf("open tty fail = %d\n", sfd);
        return -1;
    }

    const char *tmpsend = "1234567890abcdefg";
    char tmpread[128];
    //默认情况下串口文件读取会被阻塞，将文件描述符配置为非阻塞。
    //获取原来的flags
    int flags = fcntl(sfd, F_GETFL);//fd 为文件描述符
    // 设置新的flags
    flags |= O_NONBLOCK;
    // flags = flags | O_NONBLOCK;
    fcntl(sfd, F_SETFL, flags);
    
    //写
    flags = write(sfd, tmpsend, strlen(tmpsend));
    printf("%d\n", flags);

    //读
    //测试RX-TX短路，延时100ms，等待发送完成以及接受完成。
    usleep(100 * 1000);
    memset(tmpread, 0, sizeof(tmpread));
    flags = read(sfd, tmpread, sizeof(tmpread));
    printf("%d\n", flags);
    printf("read data = %s\n", tmpread);

    //关闭
    close(sfd);
}

// 输出：
// open id = 3
// 17
// 17
// read data = 1234567890abcdefg

